package com.edfu.core.commons.service.impl;

import static com.edfu.core.commons.exceptions.CommonExceptions.NULL_ARGUMENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.OBJECT_ID_NOT_PRESENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.OBJECT_TYPE_NOT_PRESENT;
import static com.edfu.core.commons.exceptions.CommonExceptions.RATING_SCORE_NOT_PRESENT;
import static com.edfu.core.testmgt.exceptions.TestExceptions.USER_NAME_NULL_OR_EMPTY;
import static com.edfu.core.testmgt.exceptions.TestExceptions.USER_NOT_FOUND;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edfu.core.commons.beans.EduObjectType;
import com.edfu.core.commons.beans.Rating;
import com.edfu.core.commons.exceptions.InvalidArgumentException;
import com.edfu.core.commons.persistence.api.IRatingPersistence;
import com.edfu.core.commons.persistence.entity.RatingEntity;
import com.edfu.core.commons.service.api.IRatingService;
import com.edfu.core.commons.util.RatingBeanTransformer;
import com.edfu.core.identitymgt.persistence.api.IIdentityMgtPersistence;
import com.edfu.core.identitymgt.persistence.entity.UserEntity;

public class RatingServiceImpl implements IRatingService {
	
	private static Logger logger = LoggerFactory.getLogger(RatingServiceImpl.class);
	
	@Autowired
	private IRatingPersistence ratingPersistence;
	
	@Autowired
	private IIdentityMgtPersistence identityMgtPersistence;
	
	private UserEntity getUserEntityAndValidate(String userId) {
		
		if (StringUtils.isBlank(userId) || StringUtils.isEmpty(userId)) {
			logger.warn("UserId is Null or Empty");
			throw new InvalidArgumentException(USER_NAME_NULL_OR_EMPTY);
		}
		
		UserEntity ue = identityMgtPersistence.getUser(userId);
		
		if (ue == null) {
			logger.warn("User not found");
			throw new RuntimeException(USER_NOT_FOUND);
		}
		return ue;
	}
	
	private void checkNull(Object obj) {
		if (obj == null) {
			throw new InvalidArgumentException(NULL_ARGUMENT);
		}
	}
	
	private void validateObjectId(long objectId) {
		if (objectId == 0) {
			logger.warn("Object ID is not present");
			throw new InvalidArgumentException(OBJECT_ID_NOT_PRESENT);
		}
	}
	
	private void validateScore(long score) {
		if (score == 0) {
			logger.warn("Rating Score is not present");
			throw new InvalidArgumentException(RATING_SCORE_NOT_PRESENT);
		}
	}
	
	private void validateObjectType(String objectType) {
		if (StringUtils.isEmpty(objectType) || StringUtils.isBlank(objectType)) {
			logger.warn("Object Type is not present");
			throw new InvalidArgumentException(OBJECT_TYPE_NOT_PRESENT);
		}
	}
	
	private void validateObjectType(EduObjectType objectType) {
		if (objectType == null) {
			logger.warn("Object Type is not present");
			throw new InvalidArgumentException(OBJECT_TYPE_NOT_PRESENT);
		}
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Rating addRating(Rating rating) {
		checkNull(rating);
		validateObjectId(rating.getObjectId());
		validateObjectType(rating.getObjectType());
		validateScore(rating.getScore());
		RatingEntity ratingEntity = RatingBeanTransformer.transformRating(rating);
		ratingEntity.setUser(getUserEntityAndValidate(rating.getUserId()));
		
		ratingEntity = ratingPersistence.addRating(ratingEntity);
		
		return RatingBeanTransformer.transformRating(ratingPersistence.addRating(ratingEntity));
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public double getMeanRating(long objectId, EduObjectType objectType) {
		validateObjectId(objectId);
		validateObjectType(objectType);
		
		return ratingPersistence.getMeanRating(objectId, objectType.toString());
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public long getRatingCount(long objectId, EduObjectType objectType) {
		validateObjectId(objectId);
		validateObjectType(objectType);
		
		return ratingPersistence.getRatingCount(objectId, objectType.toString());
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Rating> getRatings(long objectId, EduObjectType objectType) {
		validateObjectId(objectId);
		validateObjectType(objectType);
		
		List<RatingEntity> ratingEntities = ratingPersistence.getRatings(objectId, objectType.toString());
		List<Rating> ratings = new ArrayList<Rating>();
		
		if (ratingEntities != null && ratingEntities.size() > 0) {
			ratings = RatingBeanTransformer.transformRatings(ratingEntities);
		}
		
		return ratings;
	}

	@Override
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Rating getUserRating(String userId, long objectId, EduObjectType objectType) {
		validateObjectId(objectId);
		validateObjectType(objectType);
		
		return RatingBeanTransformer.transformRating(ratingPersistence.getUserRating(getUserEntityAndValidate(userId), objectId, objectType.toString()));
	}

}